package thinker;

import chess.*;

public class Evaluator {

	private static final byte NONE = ChessBoard.NONE;
	private static final byte BLACK = ChessBoard.BLACK;
	private static final byte WHITE = ChessBoard.WHITE;

	private static final int LINES = 15 + 15 + 21 + 21;
	private static final int LEN = 15;
	private static final byte[] len = new byte[LINES];
	private static int[][] score = new int[32][2];

	private byte[][] lines = new byte[LINES][LEN];

	static {
		for (int i = 0; i < 15; i++)
			len[i] = 15;
		for (int i = 0; i < 15; i++)
			len[15 + i] = 15;
		for (int i = 0; i < 10; i++)
			len[30 + i] = (byte) (5 + i);
		for (int i = 0; i <= 10; i++)
			len[40 + i] = (byte) (15 - i);
		for (int i = 0; i < 10; i++)
			len[51 + i] = (byte) (5 + i);
		for (int i = 0; i <= 10; i++)
			len[61 + i] = (byte) (15 - i);

		score[0][0] = 0; // .....
		score[1][0] = 1; // ....o
		score[2][0] = 3; // ...o.
		score[3][0] = 27; // ...oo
		score[4][0] = 9; // ..o..
		score[5][0] = 81; // ..o.o
		score[6][0] = 729; // ..oo.
		score[7][0] = 177147; // ..ooo
		score[8][0] = 9; // .o...
		score[9][0] = 81; // .o..o
		score[10][0] = 243; // .o.o.
		score[11][0] = 59049; // .o.oo
		score[12][0] = 729; // .oo..
		score[13][0] = 59049; // .oo.o
		score[14][0] = 531441; // .ooo.
		score[15][0] = 1594323; // .oooo
		score[16][0] = 1; // xo....
		score[17][0] = 27; // xo...o
		score[18][0] = 81; // xo..o.
		score[19][0] = 19683; // xo..oo
		score[20][0] = 81; // xo.o..
		score[21][0] = 2187; // xo.o.o
		score[22][0] = 59049; // xo.oo.
		score[23][0] = 1594323; // xo.ooo
		score[24][0] = 27; // xoo...
		score[25][0] = 6561; // xoo..o
		score[26][0] = 59049; // xoo.o.
		score[27][0] = 1594323; // xoo.oo
		score[28][0] = 177147; // xooo..
		score[29][0] = 1594323; // xooo.o
		score[30][0] = 1594323; // xoooo.
		score[31][0] = Node.MAX; // xooooo
		score[16][1] = 3; // .o....
		score[17][1] = 27; // .o...o
		score[18][1] = 81; // .o..o.
		score[19][1] = 59049; // .o..oo
		score[20][1] = 243; // .o.o..
		score[21][1] = 19683; // .o.o.o
		score[22][1] = 1594323; // .o.oo.
		score[23][1] = 4782969; // .o.ooo
		score[24][1] = 729; // .oo...
		score[25][1] = 177147; // .oo..o
		score[26][1] = 531441; // .oo.o.
		score[27][1] = 4782969; // .oo.oo
		score[28][1] = 1594323; // .ooo..
		score[29][1] = 4782969; // .ooo.o
		score[30][1] = 14348907; // .oooo.
		score[31][1] = Node.MAX; // .ooooo
	}

	public int calStaticValue(ChessBoard b) {
		int ans = 0;
		int v;
		int p = (Node.myColor == BLACK ? 1 : -1);
		genLines(b);
		for (int i = 0; i < LINES; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == 1 ? v : Node.MIN);
			else
				ans += p * v;
		}
		reverseColor();
		for (int i = 0; i < LINES; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == -1 ? v : Node.MIN);
			else
				ans -= p * v;
		}
		return ans;
	}

	/* Calculate the BLACK value in one line */
	private int value(byte l, byte[] line) {
		int s = 0;
		byte x = 0;
		byte y = 0;
		byte p = 0;
		int t;

		// for (int i = 0; i < l; i++)
		// if (line[i] == NONE)
		// System.out.print(".");
		// else if (line[i] == WHITE)
		// System.out.print("x");
		// else if (line[i] == BLACK)
		// System.out.print("o");
		// System.out.println();

		while (x < l) {
			y = x;
			while ((y < l) && (line[y] != WHITE))
				y++;
			p = x;
			while (x + 4 < y) {
				while ((x + 5 < y) && (line[x] == NONE))
					x++;
				t = (p == x ? 0 : 1);
				if (line[x] == NONE) {
					if (line[x + 1] == NONE) {
						if (line[x + 2] == NONE) {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// .....
									s += score[0][0];
								}
								else {
									// ....o
									s += score[1][0];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// ...o.
									s += score[2][0];
								}
								else {
									// ...oo
									s += score[3][0];
								}
							}
						}
						else {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// ..o..
									s += score[4][0];
								}
								else {
									// ..o.o
									s += score[5][0];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// ..oo.
									s += score[6][0];
								}
								else {
									// ..ooo
									s += score[7][0];
								}
							}
						}
					}
					else {
						if (line[x + 2] == NONE) {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// .o...
									s += score[8][0];
								}
								else {
									// .o..o
									s += score[9][0];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// .o.o.
									s += score[10][0];
								}
								else {
									// .o.oo
									s += score[11][0];
								}
							}
						}
						else {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// .oo..
									s += score[12][0];
								}
								else {
									// .oo.o
									s += score[13][0];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// .ooo.
									s += score[14][0];
								}
								else {
									// .oooo
									s += score[15][0];
								}
							}
						}
					}
				}
				else {
					if (line[x + 1] == NONE) {
						if (line[x + 2] == NONE) {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// o....
									s += score[16][t];
								}
								else {
									// o...o
									s += score[17][t];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// o..o.
									s += score[18][t];
								}
								else {
									// o..oo
									s += score[19][t];
								}
							}
						}
						else {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// o.o..
									s += score[20][t];
								}
								else {
									// o.o.o
									s += score[21][t];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// o.oo.
									s += score[22][t];
								}
								else {
									// o.ooo
									s += score[23][t];
								}
							}
						}
					}
					else {
						if (line[x + 2] == NONE) {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// oo...
									s += score[24][t];
								}
								else {
									// oo..o
									s += score[25][t];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// oo.o.
									s += score[26][t];
								}
								else {
									// oo.oo
									s += score[27][t];
								}
							}
						}
						else {
							if (line[x + 3] == NONE) {
								if (line[x + 4] == NONE) {
									// ooo..
									s += score[28][t];
								}
								else {
									// ooo.o
									s += score[29][t];
								}
							}
							else {
								if (line[x + 4] == NONE) {
									// oooo.
									s += score[30][t];
								}
								else {
									// ooooo
									return score[31][t];
								}
							}
						}
					}
				}
				if (s < 0)
					return (Node.MAX - 1);
				if (x + 5 == y)
					break;
				while ((x + 5 < y) && (line[x] != NONE))
					x++;
			}
			x = (byte) (y + 1);
			while ((x + 4 < l) && (line[x] == WHITE))
				x++;
			if (x + 4 >= l)
				break;
		}
		return s;
	}

	public int newChessValue(ChessBoard b, Chess ch) {
		int ans = 0;
		int s, v;
		int p = (Node.myColor == BLACK ? 1 : -1);
		byte r, c;

		for (int i = 0; i < 15; i++) {
			lines[0][i] = b.board[ch.row][i];
			lines[1][i] = b.board[i][ch.col];
		}
		len[0] = 15;
		len[1] = 15;
		s = 0;
		r = ch.row;
		c = ch.col;
		while (r > 0 && c > 0) {
			r--;
			c--;
		}
		while (r < 15 && c < 15) {
			lines[2][s] = b.board[r][c];
			s++;
			r++;
			c++;
		}
		len[2] = (byte) s;
		s = 0;
		r = ch.row;
		c = ch.col;
		while (r > 0 && c < 14) {
			r--;
			c++;
		}
		while (r < 15 && c > 0) {
			lines[3][s] = b.board[r][c];
			s++;
			r++;
			c--;
		}
		len[3] = (byte) s;

		for (int i = 0; i < 4; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == 1 ? v : Node.MIN);
			else
				ans += p * v;
		}
		reverseColor();
		for (int i = 0; i < 4; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == -1 ? v : Node.MIN);
			else
				ans -= p * v;
		}

		b.board[ch.row][ch.col] = ch.color;
		for (int i = 0; i < 15; i++) {
			lines[0][i] = b.board[ch.row][i];
			lines[1][i] = b.board[i][ch.col];
		}
		len[0] = 15;
		len[1] = 15;
		s = 0;
		r = ch.row;
		c = ch.col;
		while (r > 0 && c > 0) {
			r--;
			c--;
		}
		while (r < 15 && c < 15) {
			lines[2][s] = b.board[r][c];
			s++;
			r++;
			c++;
		}
		len[2] = (byte) s;
		s = 0;
		r = ch.row;
		c = ch.col;
		while (r > 0 && c < 14) {
			r--;
			c++;
		}
		while (r < 15 && c > 0) {
			lines[3][s] = b.board[r][c];
			s++;
			r++;
			c--;
		}
		len[3] = (byte) s;

		for (int i = 0; i < 4; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == 1 ? v : Node.MIN);
			else
				ans -= p * v;
		}
		reverseColor();
		for (int i = 0; i < 4; i++) {
			v = value(len[i], lines[i]);
			if (v == Node.MAX)
				return (p == -1 ? v : Node.MIN);
			else
				ans += p * v;
		}

		b.board[ch.row][ch.col] = ChessBoard.MARK;
		return ans;
	}

	public boolean isForbbiden(ChessBoard b) {
		boolean isForbbiden = false;
		return isForbbiden;
	}

	public int isSuccessed(ChessBoard b) {
		int ans = 0;
		int j, s;
		genLines(b);
		for (int i = 0; i < LINES; i++) {
			j = 0;
			while (j < len[i]) {
				while (j < len[i] && lines[i][j] != BLACK) {
					j++;
				}
				if (j >= len[i])
					break;
				s = 0;
				while (j < len[i] && lines[i][j] == BLACK) {
					s++;
					j++;
				}
				if (s >= 5) {
					ans = Node.myColor == BLACK ? Node.MAX : Node.MIN;
				}
			}
		}
		reverseColor();
		for (int i = 0; i < LINES; i++) {
			j = 0;
			while (j < len[i]) {
				while (j < len[i] && lines[i][j] != BLACK) {
					j++;
				}
				if (j >= len[i])
					break;
				s = 0;
				while (j < len[i] && lines[i][j] == BLACK) {
					s++;
					j++;
				}
				if (s >= 5) {
					ans = Node.myColor != BLACK ? Node.MAX : Node.MIN;
				}
			}
		}
		return ans;
	}

	private void genLines(ChessBoard b) {
		byte c;
		for (int i = 0; i < ChessBoard.ROWS; i++)
			for (int j = 0; j < ChessBoard.COLS; j++) {
				c = b.board[i][j];
				lines[i][j] = c;
				lines[15 + j][i] = c;
				if (Math.abs(i - j) <= 10)
					lines[40 - i + j][Math.min(i, j)] = c;
				if ((i + j) >= 4 && (i + j) <= 24)
					lines[47 + i + j][Math.min(i, 14 - j)] = c;
			}
	}

	private void reverseColor() {
		for (int i = 0; i < LINES; i++)
			for (int j = 0; j < len[i]; j++)
				if (lines[i][j] == BLACK)
					lines[i][j] = WHITE;
				else if (lines[i][j] == WHITE)
					lines[i][j] = BLACK;
	}

}
